clear environment

rm(list = ls())

load packages (install in you don’t have)

library(data.table)
library(ggplot2)
library(ggdist)
library(colourpicker) #addin useful for selecting colours
library(cowplot) #theme for plotting
library(data.table) #package for manipulating and computing on data
library(raincloudplots) #https://wellcomeopenresearch.org/articles/4-63

packages that have data sets in them

library(Lahman) #has data sets related to baseball (Allstar and Pitching)
library(palmerpenguins) #has data sets related to penguins (penguins)

load in data

AllstarFull from Lahman package has baseball stats.

Pitching is another data set included with Lahman package and is more comprehensive.

palmerpenguins package has data sets related to penguins.

ggplot also has data set midwest included with it. Load this by doing data(“midwest”, package = “ggplot2”)

Need to copy and then convert the data to a data table

dat = copy(Pitching)
class(dat)
[1] "data.frame"
setDT(dat)
class(dat)
[1] "data.table" "data.frame"

examine data

head(dat)
str(dat)
Classes ‘data.table’ and 'data.frame':  48399 obs. of  30 variables:
 $ playerID: chr  "bechtge01" "brainas01" "fergubo01" "fishech01" ...
 $ yearID  : int  1871 1871 1871 1871 1871 1871 1871 1871 1871 1871 ...
 $ stint   : int  1 1 1 1 1 1 1 1 1 1 ...
 $ teamID  : Factor w/ 149 levels "ALT","ANA","ARI",..: 97 142 90 111 90 136 111 56 97 136 ...
 $ lgID    : Factor w/ 7 levels "AA","AL","FL",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ W       : int  1 12 0 4 0 0 0 6 18 12 ...
 $ L       : int  2 15 0 16 1 0 1 11 5 15 ...
 $ G       : int  3 30 1 24 1 1 3 19 25 29 ...
 $ GS      : int  3 30 0 24 1 0 1 19 25 29 ...
 $ CG      : int  2 30 0 22 1 0 1 19 25 28 ...
 $ SHO     : int  0 0 0 1 0 0 0 1 0 0 ...
 $ SV      : int  0 0 0 0 0 0 0 0 0 0 ...
 $ IPouts  : int  78 792 3 639 27 3 39 507 666 747 ...
 $ H       : int  43 361 8 295 20 1 20 261 285 430 ...
 $ ER      : int  23 132 3 103 10 0 5 97 113 153 ...
 $ HR      : int  0 4 0 3 0 0 0 5 3 4 ...
 $ BB      : int  11 37 0 31 3 0 3 21 40 75 ...
 $ SO      : int  1 13 0 15 0 0 1 17 15 12 ...
 $ BAOpp   : num  NA NA NA NA NA NA NA NA NA NA ...
 $ ERA     : num  7.96 4.5 27 4.35 10 0 3.46 5.17 4.58 5.53 ...
 $ IBB     : int  NA NA NA NA NA NA NA NA NA NA ...
 $ WP      : int  7 7 2 20 0 0 1 15 3 44 ...
 $ HBP     : int  NA NA NA NA NA NA NA NA NA NA ...
 $ BK      : int  0 0 0 0 0 0 0 2 0 0 ...
 $ BFP     : int  146 1291 14 1080 57 3 70 876 1059 1334 ...
 $ GF      : int  0 0 0 1 0 1 1 0 0 0 ...
 $ R       : int  42 292 9 257 21 0 30 243 223 362 ...
 $ SH      : int  NA NA NA NA NA NA NA NA NA NA ...
 $ SF      : int  NA NA NA NA NA NA NA NA NA NA ...
 $ GIDP    : int  NA NA NA NA NA NA NA NA NA NA ...
 - attr(*, ".internal.selfref")=<externalptr> 

notes on data

playerID: Player ID code

yearID: Year

stint: player’s stint (order of appearances within a season)

teamID: Team (factor)

lgID: League ID a factor with levels AA, AL, FL, NL, PL, UA

W: Wins

L: Losses

G: Games

GS: Games Started

CG: Complete Games

SHO: Shutouts

SV: Saves IPouts Outs Pitched (innings pitched x 3)

H: Hits

ER: Earned Runs

HR: Homeruns

BB: Walks

SO: Strikeouts

BAOpp: Opponent’s Batting Average

ERA: Earned Run Average

IBB: Intentional Walks

WP: Wild Pitches

HBP: Batters Hit By Pitch

BK: Balks

BFP: Batters faced by Pitcher

GF: Games Finished R Runs Allowed

SH: Sacrifices by opposing batters

SF: Sacrifice flies by opposing batters

GIDP: Grounded into double plays by opposing batter

Core elements of a ggplot plot:

(compiled from: https://ourcodingclub.github.io/tutorials/datavis/)

geom

Geometric object which defines the type of graph you are making.

It reads your data in the aesthetics mapping to know which variables to use, and creates the graph accordingly.

Some common types are:

aes

Short for aesthetics.

Usually placed within a geom_, this is where you specify your data source and variables, AND the properties of the graph which depend on those variables.

For instance, if you want all data points to be the same colour, you would define the ‘colour =’ argument outside the aes() function; if you want the data points to be coloured by a factor’s levels (e.g. by site or species), you specify the colour = argument inside the aes().

Some common things to include in aes are:

But note that different geoms have different aesthetics available (see cheatsheet below for example)

stat

a stat layer applies some statistical transformation to the underlying data: for instance, stat_smooth(method = ‘lm’) displays a linear regression line and confidence interval ribbon on top of a scatter plot (defined with geom_point()).

theme

A set of visual parameters that control the background, borders, grid lines, axes, text size, legend position, etc.

You can use pre-defined themes (e.g., theme_complot() from the cowplot package), create your own, or use a predefined theme and overwrite only the elements you don’t like.

Examples of elements within themes are:

e.g., axis.text.y = element_text(size = 12)

e.g., axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1)

[makes the x labels at an angle]

e.g., axis.title = element_text(size = 14, face = “plain”)

e.g., panel.grid = element_blank()

[Removes the background grid lines]

e.g., plot.margin = unit(c(1,1,1,1), units = , “cm”)

[Adds a 1cm margin around the plot]

e.g., legend.text = element_text(size = 12, face = “italic”)

[Setting the font for the legend text]

e.g., legend.title = element_blank()

[Remove the legend title - useful as sometimes this is excessive and the default is to include it]

e.g., legend.position = c(0.9, 0.9)))

+theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),

axis.text.y = element_text(size = 12),

axis.title = element_text(size = 14, face = “plain”),

panel.grid = element_blank(),

plot.margin = unit(c(1,1,1,1), units = , “cm”),

legend.text = element_text(size = 12, face = “italic”),

legend.title = element_blank(),

legend.position = c(0.9, 0.9))

You define their properties with elements_…() functions. For example:

element_blank() would return something empty (ideal for removing background colour),

element_text(size = …, face = …, angle = …) lets you control all kinds of text properties.

# theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), # making the years at a bit of an angle

#try a plot of home runs over year
ggplot(dat, aes(x=yearID, y=H))+geom_point()

#equivalent to
ggplot(dat)+geom_point(aes(x=yearID, y=H))

top tip: by encircling the ggplot in parenthesis () you get to assign a plot to a variable and plot it at the same time. useful if you want to save the plot or make it into a figure, refer to it later (e.g., replot, put in a panel with other figs) etc. Example here using the same plot as above

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)))

remove grey background with +theme_bw()

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_bw())

many other themes are available

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_classic())


(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_minimal())


(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_cowplot())

you can also create your own theme!

Just write it as a function. Example here taken from: https://rpubs.com/jenrichmond/W6LL

#library(data.table)
#library(palmerpenguins)
#library(cowplot)
##library(ggplot)

theme_jen <- function () {
  
  # define font up front
  font <- "Helvetica"  
  # this theme uses theme_bw as the base 
  
  theme_bw() %+replace%   
    theme(
      #get rid of grid lines/borders
      panel.border = element_blank(), 
      panel.grid.major = element_blank(), 
      panel.grid.minor = element_blank(), 
      # add white space top, right, bottom, left
      plot.margin = unit(c(1, 1, 1, 1), "cm"), 
      # custom axis title/text/lines
      axis.title = element_text(            
        family = font,                     
        size = 14),               
      axis.text = element_text(              
        family = font,                       
        size = 12),   
      # margin pulls text away from axis
      axis.text.x = element_text(           
        margin=margin(5, b = 10)),
      # black lines
      axis.line = element_line(colour = "black", size = rel(1)), 
      # custom plot titles, subtitles, captions
      plot.title = element_text(             
        family = font,              
        size = 18,
        hjust = -0.1,
        vjust = 4),
       # custom plot subtitles
      plot.subtitle = element_text(          
        family = font,                   
        size = 14, 
        hjust = 0,
        vjust = 3),
       # custom captions
      plot.caption = element_text(           
        family = font,                   
        size = 10,
        hjust = 1,
        vjust = 2), 
      # custom legend 
      legend.title = element_text(          
        family = font,           
        size = 10,                
        hjust = 0), 
      legend.text = element_text(          
        family = font,               
        size = 8,                     
        hjust = 0), 
      #no background on legend
      legend.key = element_blank(),   
      # white background on plot
      strip.background = element_rect(fill = "white",  
                                      colour = "black", 
                                      size = rel(2)), complete = TRUE)
  
}
#source("theme_jen.R") # the script/function containing custom ggplot theme
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_jen())

add label to x and y axis plus add in various elements of theme

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + 
theme_classic()+
xlab('\nyear')+#\n adds blank line
ylab('n home runs')+ #\nadds blank line
theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), # making the years at a bit of an angle
axis.text.y = element_text(size = 12),
axis.title = element_text(size = 14, face = "plain"),                        
panel.grid = element_blank(),# Remove the background grid lines       
plot.margin = unit(c(1,1,1,1), units = , "cm"), # Add a 1cm margin around the plot
legend.text = element_text(size = 12, face = "italic"), # Setting the font for the legend text
legend.title = element_blank(), # Removing the legend title
      legend.position = c(0.9, 0.9)))

might be claner to do the same plot on mean H per year

(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID])+geom_point(aes(x=yearID, y=H)) + theme_classic()+
    xlab('\nyear')+            
    ylab('mean home runs per year')+          
    theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),     
          axis.text.y = element_text(size = 12),
          axis.title = element_text(size = 14, face = "plain"),                        
          panel.grid = element_blank(),                                   
          plot.margin = unit(c(1,1,1,1), units = , "cm"),                 
          legend.text = element_text(size = 12, face = "italic"),         
          legend.title = element_blank(),                                 
          legend.position = c(0.9, 0.9)))

add a linear trendline using geom_smooth have to specficy method for this (method=“lm” or method=lm is fine). se is added by default (can add se=F to disable this)

(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID], aes(x=yearID, y=H))+
    geom_point()+
    geom_smooth(method=lm)+
    theme_classic()+
    xlab('\nyear')+
    ylab('mean home runs per year')+
   theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), axis.text.y = element_text(size = 12), axis.title = element_text(size = 14, face = "plain"), panel.grid = element_blank(),plot.margin = unit(c(1,1,1,1), units = , "cm"), legend.text = element_text(size = 12, face = "italic"), legend.title = element_blank(),legend.position = c(0.9, 0.9)))

you can also add a specific formula in geom_smooth (e.g., y~x+x2+x3)

(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID], aes(x=yearID, y=H))+
    geom_point()+
    geom_smooth(formula=y~x+x^2+x^3)+
    theme_classic()+
    xlab('\nyear')+
    ylab('mean home runs per year')+            
    theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),
          axis.text.y = element_text(size = 12),
          axis.title = element_text(size = 14, face = "plain"),
          panel.grid = element_blank(),
          plot.margin = unit(c(1,1,1,1), units = , "cm"),
          legend.text = element_text(size = 12, face = "italic"),
          legend.title = element_blank(),
          legend.position = c(0.9, 0.9)))

facet wrap this can be used to easily plot data in panels (e.g., plot mean home runs over time for each leagueID - here I also distinguish leagues by colour)/ seeing scales = “free_y” below means the y axis can vary from plot to plot. You can also use nrow = or ncol = to specify the numbers of rows/columns

dat$yearIDfact = as.factor(dat$yearID)

(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
    geom_point()+
    facet_wrap(vars(lgID), scales = "free_y")+
    theme_classic()+
    xlab('\nyear')+            #\n adds blank line
    ylab('mean home runs per year'))

facet_grid does a similar thing but organised into columns of rows

here use rows based on teamID

(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
    geom_point()+
    facet_grid(lgID ~ .)+
    theme_classic()+
    xlab('\nyear')+            #\n adds blank line
    ylab('mean home runs per year'))

columns based on teamID

(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
    geom_point()+
    facet_grid(. ~ lgID)+
    theme_classic()+
    xlab('\nyear')+            #\n adds blank line
    ylab('mean home runs per year'))

the power of stat_summary - avoids having to feed in summary data

ggplot(dat[yearID<1875, ], aes(x=yearID, y=H))+
  stat_summary(fun.data = mean_se, geom="bar")+
  stat_summary(fun.data = mean_se, geom="errorbar", width=0.5)+
  theme_classic()

makes it super easy to then superimpose data points as can use the same data table

ggplot(dat[yearID<1875, ], aes(x=as.factor(yearID), y=H, fill=as.factor(yearID)))+
  stat_summary(fun.data = mean_se, geom="bar", show.legend = FALSE)+
  stat_summary(fun.data = mean_se, geom="errorbar", width=0.5)+
  geom_jitter(width = 0.1, show.legend = FALSE, shape=21, colour="black")+
  theme_classic()
ggplot(dat[yearID<1875, ], aes(x=as.factor(yearID), y=H, fill=as.factor(yearID)))+
  stat_summary(fun = median, geom="crossbar", show.legend = FALSE)+
  geom_jitter(width = 0.1, show.legend = FALSE, shape=21, colour="black")+
  theme_classic()
ggplot(dat, aes(x=as.factor(yearID), y=H, fill=as.factor(yearID)))+
  stat_summary(fun.data = mean_se, geom="bar", show.legend = FALSE)+
  stat_summary(fun.data = mean_se, geom="errorbar", width=0.5)+
  theme_classic()
ggplot(dat[yearID<1875], aes(x=yearID, y=H, fill=as.factor(yearID)))+
  stat_summary(fun.data=mean_se, geom="bar")+
  stat_summary(fun.data=mean_se, geom="errorbar", width=0.5)+
  theme_cowplot()+
  geom_jitter(width=0.1, shape=21)+
  theme(axis.title = element_text(size=20), axis.text = element_text(size=20), legend.position = "none")+ylim(0,800)

bar plots

bar plots with error bars and individual data points

A special subcategory as this is the most common plot I end up having to do.

Note on data wrangling

box plots

plots from models (e.g., logistic regression)

exercises

ggplot cheatsheet

ggplot cheatsheet ggplot cheatsheet

LS0tCnRpdGxlOiAiZ2dwbG90X3RpY2tzdHJpY2tzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKIyMjIGNsZWFyIGVudmlyb25tZW50CgpgYGB7cn0Kcm0obGlzdCA9IGxzKCkpCmBgYAoKIyMjIGxvYWQgcGFja2FnZXMgKGluc3RhbGwgaW4geW91IGRvbid0IGhhdmUpCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dkaXN0KQpsaWJyYXJ5KGNvbG91cnBpY2tlcikgI2FkZGluIHVzZWZ1bCBmb3Igc2VsZWN0aW5nIGNvbG91cnMKbGlicmFyeShjb3dwbG90KSAjdGhlbWUgZm9yIHBsb3R0aW5nCmxpYnJhcnkoZGF0YS50YWJsZSkgI3BhY2thZ2UgZm9yIG1hbmlwdWxhdGluZyBhbmQgY29tcHV0aW5nIG9uIGRhdGEKbGlicmFyeShyYWluY2xvdWRwbG90cykgI2h0dHBzOi8vd2VsbGNvbWVvcGVucmVzZWFyY2gub3JnL2FydGljbGVzLzQtNjMKYGBgCgojIyMgcGFja2FnZXMgdGhhdCBoYXZlIGRhdGEgc2V0cyBpbiB0aGVtCgpgYGB7cn0KbGlicmFyeShMYWhtYW4pICNoYXMgZGF0YSBzZXRzIHJlbGF0ZWQgdG8gYmFzZWJhbGwgKEFsbHN0YXIgYW5kIFBpdGNoaW5nKQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKSAjaGFzIGRhdGEgc2V0cyByZWxhdGVkIHRvIHBlbmd1aW5zIChwZW5ndWlucykKCmBgYAoKIyMjIGxvYWQgaW4gZGF0YQoKQWxsc3RhckZ1bGwgZnJvbSBMYWhtYW4gcGFja2FnZSBoYXMgYmFzZWJhbGwgc3RhdHMuCgpQaXRjaGluZyBpcyBhbm90aGVyIGRhdGEgc2V0IGluY2x1ZGVkIHdpdGggTGFobWFuIHBhY2thZ2UgYW5kIGlzIG1vcmUKY29tcHJlaGVuc2l2ZS4KCnBhbG1lcnBlbmd1aW5zIHBhY2thZ2UgaGFzIGRhdGEgc2V0cyByZWxhdGVkIHRvIHBlbmd1aW5zLgoKZ2dwbG90IGFsc28gaGFzIGRhdGEgc2V0IG1pZHdlc3QgaW5jbHVkZWQgd2l0aCBpdC4gTG9hZCB0aGlzIGJ5IGRvaW5nCipkYXRhKCJtaWR3ZXN0IiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikqCgpOZWVkIHRvIGNvcHkgYW5kIHRoZW4gY29udmVydCB0aGUgZGF0YSB0byBhIGRhdGEgdGFibGUKCmBgYHtyfQpkYXQgPSBjb3B5KFBpdGNoaW5nKQpjbGFzcyhkYXQpCnNldERUKGRhdCkKY2xhc3MoZGF0KQpgYGAKCmV4YW1pbmUgZGF0YQoKYGBge3J9CmhlYWQoZGF0KQpzdHIoZGF0KQpgYGAKCiMjIyAqKm5vdGVzIG9uIGRhdGEqKgoKKipwbGF5ZXJJRDoqKiBQbGF5ZXIgSUQgY29kZQoKKip5ZWFySUQ6KiogWWVhcgoKKipzdGludDoqKiBwbGF5ZXIncyBzdGludCAob3JkZXIgb2YgYXBwZWFyYW5jZXMgd2l0aGluIGEgc2Vhc29uKQoKKip0ZWFtSUQ6KiogVGVhbSAoZmFjdG9yKQoKKipsZ0lEOioqIExlYWd1ZSBJRCBhIGZhY3RvciB3aXRoIGxldmVscyBBQSwgQUwsIEZMLCBOTCwgUEwsIFVBCgoqKlc6KiogV2lucwoKKipMOioqIExvc3NlcwoKKipHOioqIEdhbWVzCgoqKkdTOioqIEdhbWVzIFN0YXJ0ZWQKCioqQ0c6KiogQ29tcGxldGUgR2FtZXMKCioqU0hPOioqIFNodXRvdXRzCgoqKlNWOioqIFNhdmVzIElQb3V0cyBPdXRzIFBpdGNoZWQgKGlubmluZ3MgcGl0Y2hlZCB4IDMpCgoqKkg6KiogSGl0cwoKKipFUjoqKiBFYXJuZWQgUnVucwoKKipIUjoqKiBIb21lcnVucwoKKipCQjoqKiBXYWxrcwoKKipTTzoqKiBTdHJpa2VvdXRzCgoqKkJBT3BwOioqIE9wcG9uZW50J3MgQmF0dGluZyBBdmVyYWdlCgoqKkVSQToqKiBFYXJuZWQgUnVuIEF2ZXJhZ2UKCioqSUJCOioqIEludGVudGlvbmFsIFdhbGtzCgoqKldQOioqIFdpbGQgUGl0Y2hlcwoKKipIQlA6KiogQmF0dGVycyBIaXQgQnkgUGl0Y2gKCioqQks6KiogQmFsa3MKCioqQkZQOioqIEJhdHRlcnMgZmFjZWQgYnkgUGl0Y2hlcgoKKipHRjoqKiBHYW1lcyBGaW5pc2hlZCBSIFJ1bnMgQWxsb3dlZAoKKipTSDoqKiBTYWNyaWZpY2VzIGJ5IG9wcG9zaW5nIGJhdHRlcnMKCioqU0Y6KiogU2FjcmlmaWNlIGZsaWVzIGJ5IG9wcG9zaW5nIGJhdHRlcnMKCioqR0lEUDoqKiBHcm91bmRlZCBpbnRvIGRvdWJsZSBwbGF5cyBieSBvcHBvc2luZyBiYXR0ZXIKCiMjIyAqKkNvcmUgZWxlbWVudHMgb2YgYSBnZ3Bsb3QgcGxvdDoqKgoKKGNvbXBpbGVkIGZyb206IDxodHRwczovL291cmNvZGluZ2NsdWIuZ2l0aHViLmlvL3R1dG9yaWFscy9kYXRhdmlzLz4pCgoqKmdlb20qKgoKR2VvbWV0cmljIG9iamVjdCB3aGljaCBkZWZpbmVzIHRoZSB0eXBlIG9mIGdyYXBoIHlvdSBhcmUgbWFraW5nLgoKSXQgcmVhZHMgeW91ciBkYXRhIGluIHRoZSBhZXN0aGV0aWNzIG1hcHBpbmcgdG8ga25vdyB3aGljaCB2YXJpYWJsZXMgdG8KdXNlLCBhbmQgY3JlYXRlcyB0aGUgZ3JhcGggYWNjb3JkaW5nbHkuCgpTb21lIGNvbW1vbiB0eXBlcyBhcmU6CgotICAgZ2VvbV9wb2ludCgpCgotICAgZ2VvbV9ib3hwbG90KCkKCi0gICBnZW9tX2hpc3RvZ3JhbSgpCgotICAgZ2VvbV9jb2woKQoKKiphZXMqKgoKU2hvcnQgZm9yIGFlc3RoZXRpY3MuCgpVc3VhbGx5IHBsYWNlZCB3aXRoaW4gYSBnZW9tXF8sIHRoaXMgaXMgd2hlcmUgeW91IHNwZWNpZnkgeW91ciBkYXRhCnNvdXJjZSBhbmQgdmFyaWFibGVzLCBBTkQgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGdyYXBoIHdoaWNoIGRlcGVuZCBvbgp0aG9zZSB2YXJpYWJsZXMuCgpGb3IgaW5zdGFuY2UsIGlmIHlvdSB3YW50IGFsbCBkYXRhIHBvaW50cyB0byBiZSB0aGUgc2FtZSBjb2xvdXIsIHlvdQp3b3VsZCBkZWZpbmUgdGhlICdjb2xvdXIgPScgYXJndW1lbnQgKm91dHNpZGUqIHRoZSBhZXMoKSBmdW5jdGlvbjsgaWYKeW91IHdhbnQgdGhlIGRhdGEgcG9pbnRzIHRvIGJlIGNvbG91cmVkIGJ5IGEgZmFjdG9yJ3MgbGV2ZWxzIChlLmcuIGJ5CnNpdGUgb3Igc3BlY2llcyksIHlvdSBzcGVjaWZ5IHRoZSBjb2xvdXIgPSBhcmd1bWVudCAqaW5zaWRlKiB0aGUgYWVzKCkuCgpTb21lIGNvbW1vbiB0aGluZ3MgdG8gaW5jbHVkZSBpbiBhZXMgYXJlOgoKLSAgIHgKCi0gICB5CgotICAgZmlsbAoKLSAgIGNvbG91cgoKLSAgIHNpemUKCi0gICBzaGFwZQoKKipCdXQqKiBub3RlIHRoYXQgZGlmZmVyZW50IGdlb21zIGhhdmUgZGlmZmVyZW50IGFlc3RoZXRpY3MgYXZhaWxhYmxlCihzZWUgY2hlYXRzaGVldCBiZWxvdyBmb3IgZXhhbXBsZSkKCioqc3RhdCoqCgphIHN0YXQgbGF5ZXIgYXBwbGllcyBzb21lIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIHRvIHRoZSB1bmRlcmx5aW5nCmRhdGE6IGZvciBpbnN0YW5jZSwgc3RhdF9zbW9vdGgobWV0aG9kID0gJ2xtJykgZGlzcGxheXMgYSBsaW5lYXIKcmVncmVzc2lvbiBsaW5lIGFuZCBjb25maWRlbmNlIGludGVydmFsIHJpYmJvbiBvbiB0b3Agb2YgYSBzY2F0dGVyIHBsb3QKKGRlZmluZWQgd2l0aCBnZW9tX3BvaW50KCkpLgoKKip0aGVtZSoqCgpBIHNldCBvZiB2aXN1YWwgcGFyYW1ldGVycyB0aGF0IGNvbnRyb2wgdGhlIGJhY2tncm91bmQsIGJvcmRlcnMsIGdyaWQKbGluZXMsIGF4ZXMsIHRleHQgc2l6ZSwgbGVnZW5kIHBvc2l0aW9uLCBldGMuCgpZb3UgY2FuIHVzZSBwcmUtZGVmaW5lZCB0aGVtZXMgKGUuZy4sIHRoZW1lX2NvbXBsb3QoKSBmcm9tIHRoZSBjb3dwbG90CnBhY2thZ2UpLCBjcmVhdGUgeW91ciBvd24sIG9yIHVzZSBhIHByZWRlZmluZWQgdGhlbWUgYW5kIG92ZXJ3cml0ZSBvbmx5CnRoZSBlbGVtZW50cyB5b3UgZG9uJ3QgbGlrZS4KCkV4YW1wbGVzIG9mIGVsZW1lbnRzIHdpdGhpbiB0aGVtZXMgYXJlOgoKLSAgICoqYXhpcy50ZXh0KioKCmUuZy4sIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikKCmUuZy4sIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdAo9IDEpCgpbbWFrZXMgdGhlIHggbGFiZWxzIGF0IGFuIGFuZ2xlXQoKLSAgICoqYXhpcy50aXRsZSoqCgplLmcuLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJwbGFpbiIpCgotICAgKipwYW5lbC5ncmlkKioKCmUuZy4sIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkKCltSZW1vdmVzIHRoZSBiYWNrZ3JvdW5kIGdyaWQgbGluZXNdCgotICAgKipwbG90Lm1hcmdpbioqCgplLmcuLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpCgpbQWRkcyBhIDFjbSBtYXJnaW4gYXJvdW5kIHRoZSBwbG90XQoKLSAgICoqbGVnZW5kIHRleHQqKgoKZS5nLiwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gIml0YWxpYyIpCgpbU2V0dGluZyB0aGUgZm9udCBmb3IgdGhlIGxlZ2VuZCB0ZXh0XQoKLSAgICoqbGVnZW5kLnRpdGxlKioKCmUuZy4sIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQoKW1JlbW92ZSB0aGUgbGVnZW5kIHRpdGxlIC0gdXNlZnVsIGFzIHNvbWV0aW1lcyB0aGlzIGlzIGV4Y2Vzc2l2ZSBhbmQgdGhlCmRlZmF1bHQgaXMgdG8gaW5jbHVkZSBpdF0KCi0gICAqKmxlZ2VuZCBwb3NpdGlvbioqCgplLmcuLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCgotICAgKipwdXR0aW5nIGl0IGFsbCB0b2dldGhlci4uLioqCgordGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCB2anVzdCA9IDEsCmhqdXN0ID0gMSksCgpheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAoKYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwKCnBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCgpwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLAoKbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gIml0YWxpYyIpLAoKbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAoKbGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOSkpCgpZb3UgZGVmaW5lIHRoZWlyIHByb3BlcnRpZXMgd2l0aCBlbGVtZW50c1xfLi4uKCkgZnVuY3Rpb25zLiBGb3IgZXhhbXBsZToKCmVsZW1lbnRfYmxhbmsoKSB3b3VsZCByZXR1cm4gc29tZXRoaW5nIGVtcHR5IChpZGVhbCBmb3IgcmVtb3ZpbmcKYmFja2dyb3VuZCBjb2xvdXIpLAoKZWxlbWVudF90ZXh0KHNpemUgPSAuLi4sIGZhY2UgPSAuLi4sIGFuZ2xlID0gLi4uKSBsZXRzIHlvdSBjb250cm9sIGFsbApraW5kcyBvZiB0ZXh0IHByb3BlcnRpZXMuCgpcIyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwKaGp1c3QgPSAxKSwgXCMgbWFraW5nIHRoZSB5ZWFycyBhdCBhIGJpdCBvZiBhbiBhbmdsZQoKYGBge3J9CiN0cnkgYSBwbG90IG9mIGhvbWUgcnVucyBvdmVyIHllYXIKZ2dwbG90KGRhdCwgYWVzKHg9eWVhcklELCB5PUgpKStnZW9tX3BvaW50KCkKI2VxdWl2YWxlbnQgdG8KZ2dwbG90KGRhdCkrZ2VvbV9wb2ludChhZXMoeD15ZWFySUQsIHk9SCkpCmBgYAoKdG9wIHRpcDogYnkgZW5jaXJjbGluZyB0aGUgZ2dwbG90IGluIHBhcmVudGhlc2lzICgpIHlvdSBnZXQgdG8gYXNzaWduIGEKcGxvdCB0byBhIHZhcmlhYmxlIGFuZCBwbG90IGl0IGF0IHRoZSBzYW1lIHRpbWUuIHVzZWZ1bCBpZiB5b3Ugd2FudCB0bwpzYXZlIHRoZSBwbG90IG9yIG1ha2UgaXQgaW50byBhIGZpZ3VyZSwgcmVmZXIgdG8gaXQgbGF0ZXIgKGUuZy4sIHJlcGxvdCwKcHV0IGluIGEgcGFuZWwgd2l0aCBvdGhlciBmaWdzKSBldGMuIEV4YW1wbGUgaGVyZSB1c2luZyB0aGUgc2FtZSBwbG90IGFzCmFib3ZlCgpgYGB7cn0KKHBsb3QxID0gZ2dwbG90KGRhdCkrZ2VvbV9wb2ludChhZXMoeD15ZWFySUQsIHk9SCkpKQpgYGAKCnJlbW92ZSBncmV5IGJhY2tncm91bmQgd2l0aCArdGhlbWVfYncoKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2J3KCkpCmBgYAoKbWFueSBvdGhlciB0aGVtZXMgYXJlIGF2YWlsYWJsZQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2NsYXNzaWMoKSkKCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX21pbmltYWwoKSkKCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2Nvd3Bsb3QoKSkKYGBgCgp5b3UgY2FuIGFsc28gY3JlYXRlIHlvdXIgb3duIHRoZW1lIQoKSnVzdCB3cml0ZSBpdCBhcyBhIGZ1bmN0aW9uLiBFeGFtcGxlIGhlcmUgdGFrZW4gZnJvbToKPGh0dHBzOi8vcnB1YnMuY29tL2plbnJpY2htb25kL1c2TEw+CgpgYGB7cn0KI2xpYnJhcnkoZGF0YS50YWJsZSkKI2xpYnJhcnkocGFsbWVycGVuZ3VpbnMpCiNsaWJyYXJ5KGNvd3Bsb3QpCiMjbGlicmFyeShnZ3Bsb3QpCgp0aGVtZV9qZW4gPC0gZnVuY3Rpb24gKCkgewogIAogICMgZGVmaW5lIGZvbnQgdXAgZnJvbnQKICBmb250IDwtICJIZWx2ZXRpY2EiICAKICAjIHRoaXMgdGhlbWUgdXNlcyB0aGVtZV9idyBhcyB0aGUgYmFzZSAKICAKICB0aGVtZV9idygpICUrcmVwbGFjZSUgICAKICAgIHRoZW1lKAogICAgICAjZ2V0IHJpZCBvZiBncmlkIGxpbmVzL2JvcmRlcnMKICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAjIGFkZCB3aGl0ZSBzcGFjZSB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQKICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwgMSwgMSwgMSksICJjbSIpLCAKICAgICAgIyBjdXN0b20gYXhpcyB0aXRsZS90ZXh0L2xpbmVzCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgICAgICAgICAgICAKICAgICAgICBzaXplID0gMTQpLCAgICAgICAgICAgICAgIAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgc2l6ZSA9IDEyKSwgICAKICAgICAgIyBtYXJnaW4gcHVsbHMgdGV4dCBhd2F5IGZyb20gYXhpcwogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgIAogICAgICAgIG1hcmdpbj1tYXJnaW4oNSwgYiA9IDEwKSksCiAgICAgICMgYmxhY2sgbGluZXMKICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSByZWwoMSkpLCAKICAgICAgIyBjdXN0b20gcGxvdCB0aXRsZXMsIHN1YnRpdGxlcywgY2FwdGlvbnMKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxOCwKICAgICAgICBoanVzdCA9IC0wLjEsCiAgICAgICAgdmp1c3QgPSA0KSwKICAgICAgICMgY3VzdG9tIHBsb3Qgc3VidGl0bGVzCiAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgIAogICAgICAgIGZhbWlseSA9IGZvbnQsICAgICAgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxNCwgCiAgICAgICAgaGp1c3QgPSAwLAogICAgICAgIHZqdXN0ID0gMyksCiAgICAgICAjIGN1c3RvbSBjYXB0aW9ucwogICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoICAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgICAgICAKICAgICAgICBzaXplID0gMTAsCiAgICAgICAgaGp1c3QgPSAxLAogICAgICAgIHZqdXN0ID0gMiksIAogICAgICAjIGN1c3RvbSBsZWdlbmQgCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxMCwgICAgICAgICAgICAgICAgCiAgICAgICAgaGp1c3QgPSAwKSwgCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KCAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSA4LCAgICAgICAgICAgICAgICAgICAgIAogICAgICAgIGhqdXN0ID0gMCksIAogICAgICAjbm8gYmFja2dyb3VuZCBvbiBsZWdlbmQKICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwgICAKICAgICAgIyB3aGl0ZSBiYWNrZ3JvdW5kIG9uIHBsb3QKICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMikpLCBjb21wbGV0ZSA9IFRSVUUpCiAgCn0KYGBgCgpgYGB7cn0KI3NvdXJjZSgidGhlbWVfamVuLlIiKSAjIHRoZSBzY3JpcHQvZnVuY3Rpb24gY29udGFpbmluZyBjdXN0b20gZ2dwbG90IHRoZW1lCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2plbigpKQpgYGAKCmFkZCBsYWJlbCB0byB4IGFuZCB5IGF4aXMgcGx1cyBhZGQgaW4gdmFyaW91cyBlbGVtZW50cyBvZiB0aGVtZQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIAp0aGVtZV9jbGFzc2ljKCkrCnhsYWIoJ1xueWVhcicpKyNcbiBhZGRzIGJsYW5rIGxpbmUKeWxhYignbiBob21lIHJ1bnMnKSsgI1xuYWRkcyBibGFuayBsaW5lCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpLCAjIG1ha2luZyB0aGUgeWVhcnMgYXQgYSBiaXQgb2YgYW4gYW5nbGUKYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwgICAgICAgICAgICAgICAgICAgICAgICAKcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwjIFJlbW92ZSB0aGUgYmFja2dyb3VuZCBncmlkIGxpbmVzICAgICAgIApwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAjIEFkZCBhIDFjbSBtYXJnaW4gYXJvdW5kIHRoZSBwbG90CmxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJpdGFsaWMiKSwgIyBTZXR0aW5nIHRoZSBmb250IGZvciB0aGUgbGVnZW5kIHRleHQKbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92aW5nIHRoZSBsZWdlbmQgdGl0bGUKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOSkpKQpgYGAKCm1pZ2h0IGJlIGNsYW5lciB0byBkbyB0aGUgc2FtZSBwbG90IG9uIG1lYW4gSCBwZXIgeWVhcgoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0pK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgIAogICAgeWxhYignbWVhbiBob21lIHJ1bnMgcGVyIHllYXInKSsgICAgICAgICAgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwgICAgIAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gInBsYWluIiksICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAgICAgICAgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKYWRkIGEgbGluZWFyIHRyZW5kbGluZSB1c2luZyBnZW9tX3Ntb290aCBoYXZlIHRvIHNwZWNmaWN5IG1ldGhvZCBmb3IKdGhpcyAobWV0aG9kPSJsbSIgb3IgbWV0aG9kPWxtIGlzIGZpbmUpLiBzZSBpcyBhZGRlZCBieSBkZWZhdWx0IChjYW4gYWRkCnNlPUYgdG8gZGlzYWJsZSB0aGlzKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0sIGFlcyh4PXllYXJJRCwgeT1IKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgeGxhYignXG55ZWFyJykrCiAgICB5bGFiKCdtZWFuIGhvbWUgcnVucyBwZXIgeWVhcicpKwogICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSxwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSxsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKeW91IGNhbiBhbHNvIGFkZCBhIHNwZWNpZmljIGZvcm11bGEgaW4gZ2VvbV9zbW9vdGggKGUuZy4sIHl+eCt4XjIreF4zKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0sIGFlcyh4PXllYXJJRCwgeT1IKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX3Ntb290aChmb3JtdWxhPXl+eCt4XjIreF4zKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKwogICAgeWxhYignbWVhbiBob21lIHJ1bnMgcGVyIHllYXInKSsgICAgICAgICAgICAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gInBsYWluIiksCiAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwxLDEsMSksIHVuaXRzID0gLCAiY20iKSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45LCAwLjkpKSkKYGBgCgpmYWNldCB3cmFwIHRoaXMgY2FuIGJlIHVzZWQgdG8gZWFzaWx5IHBsb3QgZGF0YSBpbiBwYW5lbHMgKGUuZy4sIHBsb3QKbWVhbiBob21lIHJ1bnMgb3ZlciB0aW1lIGZvciBlYWNoIGxlYWd1ZUlEIC0gaGVyZSBJIGFsc28gZGlzdGluZ3Vpc2gKbGVhZ3VlcyBieSBjb2xvdXIpLyBzZWVpbmcgc2NhbGVzID0gImZyZWVfeSIgYmVsb3cgbWVhbnMgdGhlIHkgYXhpcyBjYW4KdmFyeSBmcm9tIHBsb3QgdG8gcGxvdC4gWW91IGNhbiBhbHNvIHVzZSBgbnJvdyA9YCBvciBgbmNvbCA9YCB0byBzcGVjaWZ5CnRoZSBudW1iZXJzIG9mIHJvd3MvY29sdW1ucwoKYGBge3J9CmRhdCR5ZWFySURmYWN0ID0gYXMuZmFjdG9yKGRhdCR5ZWFySUQpCgoocGxvdDEgPSBnZ3Bsb3QoZGF0WywgLihIPW1lYW4oSCkpLCBieT0uKGxnSUQsIHllYXJJRCldLCBhZXMoeD15ZWFySUQsIHk9SCwgY29sb3VyPWxnSUQpKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIGZhY2V0X3dyYXAodmFycyhsZ0lEKSwgc2NhbGVzID0gImZyZWVfeSIpKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgeGxhYignXG55ZWFyJykrICAgICAgICAgICAgI1xuIGFkZHMgYmxhbmsgbGluZQogICAgeWxhYignbWVhbiBob21lIHJ1bnMgcGVyIHllYXInKSkKYGBgCgpmYWNldF9ncmlkIGRvZXMgYSBzaW1pbGFyIHRoaW5nIGJ1dCBvcmdhbmlzZWQgaW50byBjb2x1bW5zIG9mIHJvd3MKCmhlcmUgdXNlIHJvd3MgYmFzZWQgb24gdGVhbUlECgpgYGB7cn0KKHBsb3QxID0gZ2dwbG90KGRhdFssIC4oSD1tZWFuKEgpKSwgYnk9LihsZ0lELCB5ZWFySUQpXSwgYWVzKHg9eWVhcklELCB5PUgsIGNvbG91cj1sZ0lEKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBmYWNldF9ncmlkKGxnSUQgfiAuKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgICNcbiBhZGRzIGJsYW5rIGxpbmUKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykpCmBgYAoKY29sdW1ucyBiYXNlZCBvbiB0ZWFtSUQKCmBgYHtyfQoocGxvdDEgPSBnZ3Bsb3QoZGF0WywgLihIPW1lYW4oSCkpLCBieT0uKGxnSUQsIHllYXJJRCldLCBhZXMoeD15ZWFySUQsIHk9SCwgY29sb3VyPWxnSUQpKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIGZhY2V0X2dyaWQoLiB+IGxnSUQpKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgeGxhYignXG55ZWFyJykrICAgICAgICAgICAgI1xuIGFkZHMgYmxhbmsgbGluZQogICAgeWxhYignbWVhbiBob21lIHJ1bnMgcGVyIHllYXInKSkKYGBgCnRoZSBwb3dlciBvZiBzdGF0X3N1bW1hcnkgLSBhdm9pZHMgaGF2aW5nIHRvIGZlZWQgaW4gc3VtbWFyeSBkYXRhCmBgYHtyfQpnZ3Bsb3QoZGF0W3llYXJJRDwxODc1LCBdLCBhZXMoeD15ZWFySUQsIHk9SCkpKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsIGdlb209ImJhciIpKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsIGdlb209ImVycm9yYmFyIiwgd2lkdGg9MC41KSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCm1ha2VzIGl0IHN1cGVyIGVhc3kgdG8gdGhlbiBzdXBlcmltcG9zZSBkYXRhIHBvaW50cyBhcyBjYW4gdXNlIHRoZSBzYW1lIGRhdGEgdGFibGUKYGBge3J9CmdncGxvdChkYXRbeWVhcklEPDE4NzUsIF0sIGFlcyh4PWFzLmZhY3Rvcih5ZWFySUQpLCB5PUgsIGZpbGw9YXMuZmFjdG9yKHllYXJJRCkpKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJiYXIiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIsIHdpZHRoPTAuNSkrCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEsIHNob3cubGVnZW5kID0gRkFMU0UsIHNoYXBlPTIxLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpCmBgYApgYGB7cn0KZ2dwbG90KGRhdFt5ZWFySUQ8MTg3NSwgXSwgYWVzKHg9YXMuZmFjdG9yKHllYXJJRCksIHk9SCwgZmlsbD1hcy5mYWN0b3IoeWVhcklEKSkpKwogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWRpYW4sIGdlb209ImNyb3NzYmFyIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkrCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEsIHNob3cubGVnZW5kID0gRkFMU0UsIHNoYXBlPTIxLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CmdncGxvdChkYXQsIGFlcyh4PWFzLmZhY3Rvcih5ZWFySUQpLCB5PUgsIGZpbGw9YXMuZmFjdG9yKHllYXJJRCkpKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJiYXIiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIsIHdpZHRoPTAuNSkrCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CmdncGxvdChkYXRbeWVhcklEPDE4NzVdLCBhZXMoeD15ZWFySUQsIHk9SCwgZmlsbD1hcy5mYWN0b3IoeWVhcklEKSkpKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YT1tZWFuX3NlLCBnZW9tPSJiYXIiKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGE9bWVhbl9zZSwgZ2VvbT0iZXJyb3JiYXIiLCB3aWR0aD0wLjUpKwogIHRoZW1lX2Nvd3Bsb3QoKSsKICBnZW9tX2ppdHRlcih3aWR0aD0wLjEsIHNoYXBlPTIxKSsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikreWxpbSgwLDgwMCkKYGBgCgoKCiMjIyAqKmJhciBwbG90cyoqCgojIyMgKipiYXIgcGxvdHMgd2l0aCBlcnJvciBiYXJzIGFuZCBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzKioKCkEgc3BlY2lhbCBzdWJjYXRlZ29yeSBhcyB0aGlzIGlzIHRoZSBtb3N0IGNvbW1vbiBwbG90IEkgZW5kIHVwIGhhdmluZyB0bwpkby4KCioqTm90ZSBvbiBkYXRhIHdyYW5nbGluZyoqCgojIyMgKipib3ggcGxvdHMqKgoKIyMjICoqcGxvdHMgZnJvbSBtb2RlbHMgKGUuZy4sIGxvZ2lzdGljIHJlZ3Jlc3Npb24pKioKCiMjIyAqKmV4ZXJjaXNlcyoqCgoKIyMjICoqUmVzb3VyY2VzL0xpbmtzKioKCk5vbiBleGhhdXN0aXZlIGxpc3Qgb2YgbGlua3MvcmVzb3VyY2VzIEkndmUgdXNlZCBpbiB0aGUgY291cnNlIG9mCmNvbXBpbGluZyB0aGlzIG5vdGVib29rCgo8aHR0cHM6Ly9ycHVicy5jb20vamVucmljaG1vbmQvVzZMTD4KCjxodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9nZ3Bsb3QyLmh0bWw+Cgo8aHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQxLVdpdGgtUi1Db2RlLmh0bWw+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvZGF0YXZpcy8+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvZGF0YS12aXMtMi8+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvcXVhbGl0YXRpdmUvPgoKVGhpcyBoYXMgc29tZSBncmVhdCB2aWRlb3MKaHR0cHM6Ly93d3cueW91dHViZS5jb20vYy9SaWZmb21vbmFzUHJvamVjdAoKIyMjICoqZ2dwbG90IGNoZWF0c2hlZXQqKgoKIVtnZ3Bsb3QgY2hlYXRzaGVldF0oaW1hZ2VzL2dncGxvdDItY2hlYXRzaGVldGEucG5nKSAhW2dncGxvdApjaGVhdHNoZWV0XShpbWFnZXMvZ2dwbG90Mi1jaGVhdHNoZWV0Yi5wbmcpCg==